DPWorkerThread now sets up a coroutine stack.
[hom.git] / Tests / Speed Tests / Message.m
bloba33d8d36f22eb86de2cb482eb649102743afc008
1 //
2 //  Message.m
3 //  HigherOrderMessaging
4 //
5 //  Created by Ofri Wolfus on 26/08/06.
6 //  Copyright 2006 Ofri Wolfus. All rights reserved.
7 //
9 #import "Message.h"
10 #include <objc/objc-runtime.h>
11 #include <malloc/malloc.h>
12 //#import "HOMUtilities.h"
13 //#include "DPObjC-Compatibility.h"
14 #import <HigherOrderMessaging/DPObjCRuntime.h>
15 #import <Foundation/NSInvocation.h>
16 //#import "HOMInvocationBuilder.h"
20  * This file is the base of @HOM.
21  * It contains the machanism that turns a Objective-C message into a Message instance,
22  * and the entire framework relies on this.
23  * NOTE: This is not possible without the ID() macro that tricks GCC to think the MSG() macro
24  * returns an object.
25  */
28 id _sharedMessageBuilder = nil;         // No longer used
30 @interface Message (MessageBuilderSupport)
31 - (void)dealloc;
32 @end
34 #define _returnesStruct(list) (marg_getValue(list, 0, void *) != _sharedMessageBuilder)
36 @implementation Message
38 static Class autoreleasePoolCls = Nil;
40 + (void)initialize {
41         autoreleasePoolCls = objc_getClass("NSAutoreleasePool");
44 + (id)_messageWithArgs:(marg_list)list method:(MethodDescription)desc {
45         Message *m = class_createInstance(self, 0);
46         
47         if (m) {
48                 size_t len = strlen(description_getTypes(desc));
49                 char *types = calloc(len+1, sizeof(char));
50                 
51                 memcpy(types, description_getTypes(desc), len * sizeof(char));
52                 
53                 m->refCount = 1U;
54                 m->args = list;
55                 //marg_setValue(m->args, (_returnesStruct(list) ? sizeof(void *) : 0), id, nil);
56                 m->argsSize = description_getSizeOfArguments(desc);     // We cache the size of the method in order to speed up some HOM implementations
57                 m->description = description_createDescription(description_getName(desc), types);
58         }
59         
60         return [m autorelease];
63 - (void)dealloc {
64         free(args);
65         free((void *)description_getTypes(description));        // We copied the types at initialization time
66         free(description);
67         free(self);
70 - (SEL)selector {
71         return marg_getValue(args, (_returnesStruct(args) ? sizeof(void *) : 0) + sizeof(id), SEL);
74 - (marg_list)arguments {
75         return args;
78 - (unsigned)argumentsSize {
79         return argsSize;
82 - (BOOL)returnsStruct {
83         return _returnesStruct(args);
86 - (unsigned)numberOfArguments {
87 #define hom_getNumberOfArguments(s) sel_getNumberOfArguments(s)
88         return hom_getNumberOfArguments([self selector]);
91 - (const char *)types {
92         return description_getTypes(description);
95 - (MethodDescription)methodDescription {
96         return description;
99 - (id)forward:(SEL)sel :(marg_list)arglist {
100         return nil;
103 - (id)retain {
104         ++refCount;
105         return self;
108 - (id)autorelease {
109         if (autoreleasePoolCls)
110                 [autoreleasePoolCls addObject:self];
111         
112         return self;
115 - (void)release {
116         --refCount;
117         
118         if (refCount == 0U)
119                 [self dealloc];
122 - (unsigned)retainCount {
123         return refCount;
126 - (NSString *)description {
127         return [NSString stringWithFormat:@"<0x%x> Message: \"%s\"", self, sel_getName([self selector])];
130 - (id)sendTo:(id)receiver {
131         return objc_msgSendv(receiver, [self selector], argsSize, args);
134 @end
136 @implementation Message (CocoaConventions)
138 - (id)invocationWithTarget:(id)target {
139         /*NSInvocation *r;
140         id builder = [HOMInvocationBuilder builderForTarget:target];
141         
142         r = [self sendTo:builder];
143         [builder dealloc];
144         return r;*/
145         return nil;
148 @end
150 //=================================================================================//
151 //=================================================================================//
152 //=================================================================================//
154 // Finds a method description for the given selector, which has the largest arguments size.
155 // The returned description must be freed properly.
156 MethodDescription _hom_findMethodForSelector(SEL sel) {
157         // Holds all our cached method descriptions
158         static CFMutableDictionaryRef _methodsCache = NULL;
159         
160         static int                      classCount = 0;         // The number of classes that was in our previous search
161         static Class            *classes = NULL;        // A block with pointers to all classes
162         int                                     count;
163         MethodDescription       result;
164         Method                          meth = NULL;
165         NSString                        *key = NSStringFromSelector(sel);
166         
167         // Like in the ObjC runtime, thread synchronization is always active
168         @synchronized ((NSDictionary *)_methodsCache) {
169                 // Set up our cache dictionary if it wasn't already active
170                 if (!_methodsCache)
171                         _methodsCache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
172                 
173                 // Look for a cached version of our method.
174                 // If no new classes were registered since the last time we did a search, this method will be used
175                 result = (MethodDescription)CFDictionaryGetValue(_methodsCache, key);
176                 
177                 // Get the current number of classes registered with the runtime
178                 count = objc_getClassList(NULL, 0);
179                 
180                 /*
181                  * We work only if new classes were registered since our last search.
182                  * NOTE: When unloading of classes is implemened this check may not be enough
183                  * if some classes were added, and some others were removed but the total number of classes remained the same.
184                  * Even in this situation though, the chances are very small the new classes added new method for existing selector(s)
185                  * with a different size of total arguments.
186                  */
187                 if (count != classCount) {
188                         // Allocate enough memory for pointers to all classes available
189                         if (!classes)
190                                 classes = malloc(sizeof(Class) * count);
191                         else
192                                 classes = reallocf(classes, sizeof(Class) * count);
193                         
194                         if (classes) {
195                                 // Remember the current number of classes
196                                 classCount = count;
197                                 
198                                 // Get all available classes from the runtime
199                                 objc_getClassList(classes, classCount);
200                                 
201                                 // Clean up our cache
202                                 CFDictionaryRemoveAllValues(_methodsCache);
203                         }
204                         
205                         // Free our previous cached method
206                         if (result) {
207                                 //free(result->method_types);
208                                 free(result);
209                                 result = NULL;
210                         }
211                 }
212                 
213                 if (!result) {
214                         Class           cls;
215                         int                     i;
216                         Method          m = NULL;
217                         unsigned        argsSize = 0U;
218                         
219                         /*
220                          * Loop through all classes and search for a method with a matching selector
221                          * This is a very long loop and that's why we use our cache if possible
222                          * NOTE: Since method lookups scans through all super classes, we can cut down the number of lookups
223                          * by remembering which super classes were already searched and skip those.
224                          */
225                         for (i = 0; i < classCount; i++) {
226                                 cls = classes[i];
227                                 
228                                 // First, attempt to get an instance method
229                                 m = class_getInstanceMethod(cls, sel);
230                                 
231                                 // If it's not available, try a class method
232                                 if (!m)
233                                         m = class_getClassMethod(cls, sel);
234                                 
235                                 /*
236                                  * If we found a method, we need to get the size of its arguments.
237                                  * Since we can't know which method will be used in practice, we'll pick the one with the largest
238                                  * size so that we don't accidentally cut some arguments.
239                                  */
240                                 if (m) {
241                                         unsigned s = method_getSizeOfArguments(m);
242                                         
243                                         if (s > argsSize) {
244                                                 meth = m;
245                                                 argsSize = s;
246                                         }
247                                 }
248                         }
249                         
250                         if (meth) {
251                                 //unsigned len = strlen(meth->method_types);
252                                 
253                                 // Allocate a new method, and copy the method we just found.
254                                 // NOTE: The IMP of our method always points to NULL.
255                                 /*result = calloc(1, sizeof(struct objc_method));
256                                 result->method_name = sel;
257                                 result->method_types = malloc(sizeof(char) * (len + 1));
258                                 strcpy(result->method_types, meth->method_types);*/
259                                 result = dp_copyMethodDescription(meth);
260                                 
261                                 // Cache our method description
262                                 CFDictionarySetValue(_methodsCache, key, result);
263                         }
264                 }
265         }
266         
267         return result;
270 //=================================================================================//
271 //=================================================================================//
272 //=================================================================================//
275  * The MessageBuilder class is a special class that responds to has no methods.
276  * Every message sent to it will be packed and returned as a Message instance.
277  * Unlike normal singleton objects, there is no instance of this class and messages will be sent
278  * to the class directly.
279  */
280 @interface MessageBuilder {
281         id isa;
283 @end
285 @implementation MessageBuilder
287 // Set up the public pointer to ourself
288 + (void)load {
289         _sharedMessageBuilder = self;
292 // The runtime automatically calls this method, so we make sure it won't get accidentally forwarded.
293 + (void)initialize {
296 // Catche all messages and turn them into Message instances
297 - (id)forward:(SEL)sel :(marg_list)args {
298         marg_list                       list;
299         MethodDescription       d = _hom_findMethodForSelector(sel);
300         
301         if (d) {
302                 //marg_malloc(list, m);
303                 list = dp_marg_malloc(description_getSizeOfArguments(d));
304                 memcpy(list, args, malloc_size(list));
305                 
306                 return [Message _messageWithArgs:list method:d];
307         }
308         
309         return nil;
312 @end